package client;

import java.io.IOException;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

import server.MazeData;
import common.*;

public class MazeClient implements ClientCallback, Serializable{
	
	private static final long serialVersionUID = 227L;
	
	public Maze maze;
	public GameState game_data;
	private int id;
	private boolean started;
	private int treasuresPreviousCollected = 0;
	private Timer timer;; 
	private long lastActiveTime = 0;
	public boolean isStarted() {
		return started;
	}

	public MazeClient(Maze maze) {
		this.maze = maze;
		started = false;
		timer = new Timer(); 
		try {
			UnicastRemoteObject.exportObject(this, 0);
		} catch (RemoteException e) {
			System.out.println("Fails to allow the client to be call backed.");
			e.printStackTrace();
		}
	}
	// method getChar(): retrieve a single char from System.in
	static public char getChar() throws IOException {
		char ch = (char) System.in.read();
		flushInput(); // This clears out System.in
		return ch;
	}

	// method flushInput: reads System.in until newline
	static public void flushInput() throws IOException {
		while ((char) System.in.read() != '\n')
			; // do nothing
	}
	
	public void joinGame() {
		try {
			id = maze.joinGame(this);
			if (id == -1) {
				System.err.println("Game has started, can't join.");
				System.exit(1);
			} else {
				System.out.print("client " + id + "  ");
				System.out.println("Waiting for the game to start...");
			}
		} catch (RemoteException e) {
			System.out.println("Join Game Exception:");
			e.printStackTrace();
		}
	}
	
	public void move( String direction) {
		try {
			lastActiveTime = System.currentTimeMillis();	
			this.treasuresPreviousCollected = game_data.collected[this.id];
			game_data = maze.move(this.id, direction);
			drawScreen();
		} catch (RemoteException e) {
			//System.out.println("Move Exception:");
			//e.printStackTrace();
			this.timer.cancel();
			System.out.println("server is down!");
			System.exit(0);
		}
	}
	
	public void startGame(GameState state) {
		this.game_data = state;
		System.out.println("game started");
		drawScreen();
		timer.schedule(new HeartBeatTask(this), 5000, 5000);
		synchronized(this)
		{
			this.started = true;
			this.notify();
		}
	}
	
	private int treasuresLeft() {
		int ret = 0;
		for (int i = 0; i < game_data.dimension; i++) {
			for (int j = 0; j < game_data.dimension; j++) {
				ret += game_data.grid[i][j];
			}
		}
		return ret;
	}
	private void drawScreen() {
		System.out.println("You: * \t The others: x \t Blank grid: 0 \t Treasure: $");
		int treasuresCollected = game_data.collected[id];		
		int treasuresLeft = this.treasuresLeft();
		int nNewlyCollected = game_data.collected[this.id ]  -  this.treasuresPreviousCollected;
		System.out.println("Your treasures:" + treasuresCollected + "\t Treasures left:" + treasuresLeft);
		System.out.println("You collected " +nNewlyCollected + " treasures!" );
		System.out.println("Your location: (" + game_data.location[id].x + ", " + game_data.location[id].y + ")" +
				"\t Number of Players: " + game_data.num_of_player);
		System.out.println();
		char[][] grid = new char[game_data.dimension][game_data.dimension];
		for (int i = 0; i < game_data.dimension; i++) {
			for (int j = 0; j < game_data.dimension; j++) {
				if (game_data.grid[i][j] > 0) {
					grid[i][j] = '$';
				} else {
					grid[i][j] = '0';
				}
			}
		}
		for (int i = 0; i < game_data.num_of_player; i++) {
			int x = game_data.location[i].x;
			int y = game_data.location[i].y;
			grid[x][y] = (id == i ? '*' : 'x');
		}
		for (int i = 0; i < game_data.dimension; i++) {
			for (int j = 0; j < game_data.dimension; j++) {
				System.out.print(grid[i][j]);
			}
			System.out.println();
		}
		System.out.println();
	}
	

	public boolean isPlayerCrash() {
		return true;
	}
	
	public static void main(String args[]) {
//		if (System.getSecurityManager() == null) {
//            System.setSecurityManager(new SecurityManager());
//        }
        try {
            String name = "MazeServer";
            Registry registry = LocateRegistry.getRegistry(args[0]);
            Maze maze = (Maze) registry.lookup(name);
            MazeClient maze_client = new MazeClient(maze);
            maze_client.joinGame();     

            //here should wailt a signal            
        	while (maze_client.isStarted() != true) {
        		//System.out.println("not started!");
        		synchronized(maze_client)
        		{
        			maze_client.wait(200);
        		}
			}

			

			char userEntry; // User's entry

			while ((userEntry = getChar()) != 'Q') {
				if (userEntry == 'w'
					|| userEntry == 's' 
					|| userEntry == 'a'
					|| userEntry == 'd') {					
								
					maze_client.move(userEntry + "");					
				}				
			}

            
        } catch (Exception e) {
            System.err.println("MazeClient exception: ");
            e.printStackTrace();
        }
	}
	class HeartBeatTask extends TimerTask
	{
		
		private MazeClient parent; 
		public HeartBeatTask(MazeClient parent)
		{
			this.parent = parent;
		}
		 public void run() {
			 
		     //System.out.println("Heartbeat time's up for game starting!");
			 long time = System.currentTimeMillis();
			 if(time - parent.lastActiveTime > 5000)
			 {
				 this.parent.move("NoMove");
				 //System.out.println("Heartbeat time's up for game starting!");
			 }
		      //timer.cancel(); //Not necessary because we call System.exit
		      //System.exit(0); //Stops the AWT thread (and everything else)
		    }
	}
	
}
